package rps.client.ai;

import static rps.game.data.FigureKind.FLAG;
import static rps.game.data.FigureKind.PAPER;
import static rps.game.data.FigureKind.ROCK;
import static rps.game.data.FigureKind.SCISSORS;
import static rps.game.data.FigureKind.TRAP;
import static rps.game.data.FigureKind.HIDDEN;
import static java.util.concurrent.TimeUnit.NANOSECONDS;

import java.rmi.RemoteException;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
import java.util.Iterator;

import rps.client.GameListener;
import rps.client.ui.GameLogic;
import rps.game.Game;
import rps.game.Movement;
import rps.game.data.AttackResult;
import rps.game.data.Figure;
import rps.game.data.FigureKind;
import rps.game.data.Move;
import rps.game.data.Player;

/**
 * This class contains an advanced AI, that should participate in the
 * tournament.
 */
public class TournamentAi implements GameListener {

	private final int maxDurationForMoveInMilliSeconds;
	private final int maxDurationForAllMovesInMilliSeconds;
	private Player player = new Player("G.O.T.T.AI (#72)");
	private Game game;
	private boolean gameOver = false;
	private boolean[] opponentAssignment = new boolean[14];
	private Collection<Integer> fieldsVisited = new ArrayList<Integer>();
	private FigureKind[] initAs = new FigureKind[42];
	private FigureKind[] initRow1 = new FigureKind[]{PAPER, PAPER, ROCK, ROCK, TRAP, SCISSORS, SCISSORS};
	private FigureKind[] initRow2 = new FigureKind[]{PAPER, PAPER, ROCK, ROCK, FLAG, SCISSORS, SCISSORS};
	
	private void setInitAssignment(){
		for( int i = 0; i < 7; i++){
			int rN = (int)(Math.random()*initRow1.length);
			FigureKind a = initRow1[i];
			initRow1[i] = initRow1[rN];
			initRow1[rN] = a;
		}
		for( int i = 0; i < 7; i++){
			int rN = (int)(Math.random()*initRow2.length);
			FigureKind a = initRow2[i];
			initRow2[i] = initRow2[rN];
			initRow2[rN] = a;
		}
		for(int i = 28; i < 35; i++)
			initAs[i] = initRow1[i-28];
		for(int i = 35; i < 42; i++)
			initAs[i] = initRow2[i-35];
	}
	
	private FigureKind randomFigure(){
		int randomNumber = 1+(int)(Math.random()*3);
		FigureKind myFigure;
		System.out.println(randomNumber);
		switch(randomNumber){
		case 1:
			myFigure = FigureKind.ROCK;
			break;
		case 2:
			myFigure = FigureKind.PAPER;
			break;
		case 3:
			myFigure = FigureKind.SCISSORS;
			break;
		default:
			myFigure = FigureKind.ROCK;
		}
		return myFigure;
	}

	public TournamentAi(int maxDurationForMoveInMilliSeconds, int maxDurationForAllMovesInMilliSeconds) {
		this.maxDurationForMoveInMilliSeconds = maxDurationForMoveInMilliSeconds;
		this.maxDurationForAllMovesInMilliSeconds = maxDurationForAllMovesInMilliSeconds;
		// TODO Auto-generated constructor stub
	}

	@Override
	public void chatMessage(Player sender, String message) throws RemoteException {
	}

	@Override
	public void provideInitialAssignment(Game game) throws RemoteException {
		this.game = game;
		setInitAssignment();
		game.setInitialAssignment(player, initAs);

	}

	@Override
	public void provideInitialChoice() throws RemoteException {
		game.setInitialChoice(player, randomFigure());

	}

	@Override
	public void startGame() throws RemoteException {
		System.out.println("Ui gestartet");

	}

	@Override
	public void provideNextMove() throws RemoteException {
		long moveCalculationStartedAt = System.nanoTime();
		Figure[] gameField = new Figure[42];
		gameField = game.getField();
		for(int i = 0; i < 14; i++){
			if(gameField[i] == null){
				this.opponentAssignment[i] = true;
			}
		}
		Collection<Move> moves = new Movement(game, player).getPosMoves();
		Collection<Move> bestMoves = new ArrayList<Move>();
		Move nextMove = null;
		Move tempMove = null;
		int scoreA = 0;
		int scoreB = 0;
		game.sendMessage(player, "-----------------");
		Iterator<Move> it = moves.iterator();
		for (int i = 0; i < moves.size(); i++) {
			tempMove = it.next();
			scoreB = scoreMove(tempMove);
		//	game.sendMessage(player, i + ". " +  tempMove + " Score: " + scoreB);
			if(scoreB == scoreA){
				bestMoves.add(tempMove);
			}
			else if(scoreB > scoreA){
				bestMoves.clear();
				bestMoves.add(tempMove);
				scoreA = scoreB;
				//System.out.println(tempMove + " Score: " + scoreA);
			}
		}
			if (bestMoves.isEmpty()) {
				bestMoves.add(tempMove);
			}
		int randomNumber = 1+(int)(Math.random()*bestMoves.size());
		Iterator<Move> iter = bestMoves.iterator();
		for (int i = 0; i < randomNumber; i++) {
			nextMove = iter.next();
		}
		game.sendMessage(player, player.getNick() + " zieht von " + nextMove.getFrom() + " nach " + nextMove.getTo());
		game.move(player, nextMove.getFrom(), nextMove.getTo());
		if(nextMove.getTo() < 7)
			fieldsVisited.add(nextMove.getTo());
		long time = NANOSECONDS.toMillis(System.nanoTime() - moveCalculationStartedAt);
		System.out.println("Zeit zur Zugberechnung: " + time + "ms");
	}
	
	private int scoreMove(Move move){
		int score = 0;
		Figure[] gameField = new Figure[42];
		Player opponent = null;
		try {
			gameField = game.getField();
			opponent = game.getOpponent(player);
		} catch (RemoteException e1) {
			e1.printStackTrace();
		}
		int fromIndex = move.getFrom();
		int toIndex = move.getTo();
		FigureKind fromKind = null;
		FigureKind toKind = null;
		boolean isOpponent = false;
		boolean discovered = false;
		boolean discoveredOpp = false;
		fromKind = gameField[fromIndex].getKind();
			if(gameField[fromIndex].isDiscovered())
				discovered = true;
			if(gameField[toIndex] != null){
				isOpponent = true;
				discoveredOpp = gameField[toIndex].isDiscovered();
				toKind = gameField[toIndex].getKind();
			}
		for(int i = 0; i < 6; i++) {
			if(i*7 <= toIndex && toIndex <= ((i+1)*7-1) && (toKind == null))
				score = (6-i)*11;
		}
		if (toIndex/7 < fromIndex/7) {
			score = score + 10;
		} else if (toIndex/7 > fromIndex/7) {
			score = score -15;
		}
		if(toIndex < 14 && !opponentAssignment[toIndex]){
			score = 90;
		}
		if(discoveredOpp){
			if(fromKind.attack(toKind).equals(AttackResult.WIN)){
				score = score + 100;
			}
			else if(fromKind.attack(toKind).equals(AttackResult.LOOSE)){
				score = score - 100;
			}
		}
		//if (surroundFlagFields().contains(fromIndex) && !surroundFlagFields().contains(toIndex)){
		//	score = score - 10;
		//}
		if (surroundFlagFields().contains(toIndex) && isOpponent) {
			if (discoveredOpp) {
				if (fromKind.attack(toKind).equals(AttackResult.WIN)) {
					score += 200;
				} else if (fromKind.attack(toKind).equals(AttackResult.DRAW)) {
					score += 120;
				}
			} else {
				score += 110;
			}
		}
		else if(toKind == null){
			if (surroundFlagFields().contains(toIndex) && !surroundFlagFields().contains(fromIndex)) {
				score = score + 10;
			}
			Collection<Integer> fields = surroundingFields(toIndex);
			for(Iterator<Integer> iter = fields.iterator(); iter.hasNext();){
				int currentField = iter.next();
				Collection<Figure> figures = new ArrayList<Figure>();
				if(gameField[currentField] != null){
					figures.add(gameField[currentField]);
					if(gameField[currentField].belongsTo(player)){
						if((gameField[currentField].getKind().attack(fromKind).equals(AttackResult.WIN)
								|| fromKind.attack(gameField[currentField].getKind()).equals(AttackResult.WIN))
								&& !gameField[currentField].getKind().equals(FLAG)
								&& !gameField[currentField].getKind().equals(TRAP)){
							for(int i = 0; i < 6; i++){
								if(i*7 < toIndex && toIndex < ((i+1)*7-1) && toKind == null)
									score = score + (6-i)*6;
							}
						}
					}
					else if(gameField[currentField].isDiscovered() && fromKind.attack(gameField[currentField].getKind()).equals(AttackResult.WIN)){
						score = score + 30;
					}
					else if(fromKind.attack(gameField[currentField].getKind()).equals(AttackResult.LOOSE)){
						score = score - 30;
					}
				}
				if(figures.contains(new Figure(FLAG, player)) && figures.contains(new Figure(HIDDEN, opponent))){
					score = score + 50;
				}
			}
		}
		if(fieldsVisited.contains(toIndex)){
			score = score - 3;
		}
		return score;
	}
	
	private Collection<Integer> surroundFlagFields () {
		Collection<Integer> ret = new ArrayList<Integer>();
		int flagPos = getFlagPos();
		if (flagPos == 35) {
			ret.add(28);
			ret.add(29);
		} else if (flagPos == 41) {
			ret.add(33);
			ret.add(34);
		} else {
			ret.add(flagPos-6);
			ret.add(flagPos-7);
			ret.add(flagPos-8);
		}
		
		return ret;
	}
	
	private int getFlagPos () {
		int i = 28;
		Figure[] field = null;
		try {
			field = game.getField();
		} catch (RemoteException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}
		while (field[i] == null || !(field[i].getKind().equals(FLAG))) {
			i++;
		}
		return i;
	}
	private Collection<Integer> surroundingFields(int from){
		Collection<Integer> fields = new ArrayList<Integer>();
		if (from >= 7) {	
			fields.add(from-7);
		}
		if (from <= 34) {
			fields.add(from+7);
		}
		if (!(from % 7 == 6) && from >= 0) {
			fields.add(from+1);
		}
		if (!(from % 7 == 0) && from < 42) {
			fields.add(from-1);
		}
		return fields;
	}

	@Override
	public void figureMoved() throws RemoteException {
		// TODO Auto-generated method stub

	}

	@Override
	public void figureAttacked() throws RemoteException {
		// TODO Auto-generated method stub

	}

	@Override
	public void provideChoiceAfterFightIsDrawn() throws RemoteException {
		game.setUpdatedKindAfterDraw(player, randomFigure());

	}

	@Override
	public void gameIsLost() throws RemoteException {
		this.gameOver = true;
	}

	@Override
	public void gameIsWon() throws RemoteException {
		this.gameOver = true;
	}

	@Override
	public void gameIsDrawn() throws RemoteException {
		this.gameOver = true;
	}

	@Override
	public String toString() {
		return "Tournament AI";
	}

	@Override
	public Player getPlayer() throws RemoteException {
		return this.player;
	}
}